package org.es.config;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestClientBuilder.RequestConfigCallback;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import javax.net.ssl.*;

/**
 * <p>Title: EsConfiguration.java</p>
 * <p>Description: ES连接池配置器</p>
 * <p>Company: LANWON</p>
 * @author LiuYZ
 * @date 2019年9月27日
 * @version 1.0
 */

@Configuration
public class EsConfiguration {

	@Value("${elasticsearch.ips}")
	private String[] ips;

/**
	 * 连接超时时间
	 */

	@Value("${elasticsearch.connectTimeOut:90000}")
	private int connectTimeOut = 90000;

/**
	 * 连接超时时间
	 */

	@Value("${elasticsearch.socketTimeOut:90000}")
	private int socketTimeOut = 90000;

	/**
	 * 获取连接的超时时间
	 */
	@Value("${elasticsearch.connectionRequestTimeOut:90000}")
	private int connectionRequestTimeOut = 90000;

	@Value("${elasticsearch.userName:admin}")
	private String username;

	@Value("${elasticsearch.password:LANwang@0408}")
	private String password;

	/**
	 * 最大连接数
	 */

	@Value("${elasticsearch.maxConnectNum:100}")
	private int maxConnectNum = 600;


	/**
	 * 最大路由连接数
	 */

	@Value("${elasticsearch.maxConnectPerRoute:600}")
	private int maxConnectPerRoute = 600;


	/**
	 * 这个close是调用RestHighLevelClient中的close
	 * @return
	 */
	@Bean(destroyMethod="close")
	@Scope("singleton")
	public RestHighLevelClient client() {

		/**
		 * 忽略所有证书，跳过证书校验环节进行连接
		 * 构造TrustManager，使用默认X509TrustManager，不重写任何方法，相当于忽略所有相关操作。
		 * 构造SSLContext：使用第一步的TrustManager为参数，默认方法构造SSLContext。
		 */
		TrustManager[] trustAllCerts = new TrustManager[] {
				new X509TrustManager() {
					@Override
					public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
					}

					@Override
					public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
					}

					@Override
					public X509Certificate[] getAcceptedIssuers() {
						return null;
					}
				}
		};

		List<Object> host = Stream.of(ips).map(this::createHttpHost).collect(Collectors.toList());
		HttpHost[] httpHosts = new HttpHost[host.size()];
		host.toArray(httpHosts);

		RestClientBuilder builder = RestClient.builder(httpHosts);

		final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
		SSLContext sc = null;
		try{
			sc = SSLContext.getInstance("SSL");
			sc.init(null, trustAllCerts, new SecureRandom());
		}catch(KeyManagementException e){
			e.printStackTrace();
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		}
		SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sc, new NullHostNameVerifier());

		//SecuredHttpClientConfigCallback httpClientConfigCallback = new SecuredHttpClientConfigCallback(sessionStrategy,credentialsProvider);

		//builder.setHttpClientConfigCallback(httpClientConfigCallback);

		// 异步httpclient连接延时配置
		builder.setRequestConfigCallback(new RequestConfigCallback() {
			@Override
			public Builder customizeRequestConfig(Builder requestConfigBuilder) {
				requestConfigBuilder.setConnectTimeout(connectTimeOut);
				requestConfigBuilder.setSocketTimeout(socketTimeOut);
				requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);
				return requestConfigBuilder;
			}
		});
		builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
			@Override
			public HttpAsyncClientBuilder customizeHttpClient(final HttpAsyncClientBuilder httpAsyncClientBuilder) {
				httpAsyncClientBuilder.setMaxConnTotal(maxConnectNum);
				httpAsyncClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
				// enable SSL / TLS
				httpAsyncClientBuilder.setSSLStrategy(sessionStrategy);
				// enable user authentication
				if (credentialsProvider != null) {
					httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
				}
				return httpAsyncClientBuilder;
			}
		});

		RestHighLevelClient client = new RestHighLevelClient(builder);

		return client;
	}

	private Object createHttpHost(String ip) {
		return HttpHost.create(ip);
	}

	public static class NullHostNameVerifier implements HostnameVerifier {
		@Override
		public boolean verify(String arg0, SSLSession arg1) {
			return true;
		}
	}

}
